#ifndef AST_VISITOR_H
#define AST_VISITOR_H
#pragma once

/**
  @brief The AstVisitor class 
  
  @date Auto generated on {% now "yyyy/MM/dd hh:mm:ss" %}
*/

#include <type_traits>
#include "macros.h"
#include "utils.h"
#include "cpp-gen/FormalParser.h"
#include "dummy_node.h"

#define RETURN_DEFAULT return defaultResult();
#define IF_NULLPTR_RETURN_DEFAULT(x) \
    if (!x)                          \
    RETURN_DEFAULT

namespace antlr4::tree
{
    class ParseTree;
    class TerminalNode;
    class ErrorNode;
}

using antlr4::RuleContext;
using antlr4::Token;
using antlr4::tree::ErrorNode;
using antlr4::tree::ParseTree;
using antlr4::tree::TerminalNode;

namespace cyclone::parser
{
    template <typename Q>
    bool is(ParseTree *ptr)
    {
        return nullptr != dynamic_cast<Q *>(ptr);
    };
    template <typename T>
    class AstVisitor
    {
        CHECK_TYPE_HAS_VISIBLE_PROPERTY_NAME(isArrayList);

        template <typename Q>
        T dispatchHelper(ParseTree *ptr)
        {
#ifdef CYCLONE_PARSER_DEBUG
            if (auto target = dynamic_cast<Q *>(ptr))
                return visit(target);
            else
            {
                std::cout << "error , cannot cast to " << typeid(Q *).name();
                return defaultResult();
            }
#endif // CYCLONE_PARSER_DEBUG
            return visit(reinterpret_cast<Q *>(ptr));
        }
        using RouteMap = std::unordered_map<short, std::pair<decltype(&AstVisitor<T>::dispatchHelper<ParseTree>), decltype(&is<ParseTree>)>>;
        static const RouteMap routing_;

    public:
        virtual ~AstVisitor() = default;
        virtual T visit(ParseTree *node) = 0;
        virtual T visit(TerminalNode *node) = 0;
        virtual T visit(DummyNode *node) = 0;
        virtual T visit(ErrorNode *node) = 0;
        virtual T visit(Token *node) = 0;
        virtual T defaultResult() = 0;

        template <typename Q>
        typename std::enable_if_t<(!std::is_base_of_v<ParserRuleContext, Q>), T>
        visitAnonymousNode(Q *tree)
        {
            IF_NULLPTR_RETURN_DEFAULT(tree);
            return visit(tree);
        }

        template <typename Q>
        typename std::enable_if_t<std::is_base_of_v<ParserRuleContext, Q>, T>
        visitAnonymousNode(Q *tree)
        {
            IF_NULLPTR_RETURN_DEFAULT(tree)
            if (tree->getContextId() == DummyNode::dummyNodeContentId())
                return visit(reinterpret_cast<DummyNode *>(tree));
            std::vector<T> _resultList;
            for (auto child : tree->children)
            {
                const auto id = child->getContextId();
                if (id == DummyNode::dummyNodeContentId())
                     _resultList.push_back(visit(reinterpret_cast<DummyNode *>(child)));
                if (Utils::shouldIgnore(child, id))
                    continue;
                switch (id)
                {
                case INVALID_INDEX:
                    break;
                case ERROR_NODE_INDEX:
                    _resultList.push_back(visit(reinterpret_cast<ErrorNode *>(child)));
                    break;
                case TERMINAL_NODE_INDEX:
                    _resultList.push_back(visit(reinterpret_cast<TerminalNode *>(child)));
                    break;
                default:
                    if (routing_.end() != routing_.find(id))
                        _resultList.push_back(std::bind(routing_.at(id).first, this, child)());
                }
            }
            if constexpr (CheckisArrayList<Q>::value)
                return _resultList;
            return 1 == _resultList.size() ? _resultList.at(0) : _resultList;
        }

        template <typename Q>
        typename std::enable_if_t<!std::is_base_of_v<ParserRuleContext, Q>, T>
        dispatch(Q *tree)
        {
            IF_NULLPTR_RETURN_DEFAULT(tree)
            return visit(tree);
        }

        template <typename Q>
        typename std::enable_if_t<std::is_base_of_v<ParserRuleContext, Q>, T>
        dispatch(Q *tree)
        {
            if (!tree)
            {
                if constexpr (CheckisArrayList<Q>::value)
                    return T::array();
                return defaultResult();
            }
            const auto ruleIndex = tree->getContextId();
            return ruleIndex == DummyNode::dummyNodeContentId() ? visit(reinterpret_cast<DummyNode *>(tree)) : (routing_.end() == routing_.find(ruleIndex) ? defaultResult() : std::bind(routing_.at(ruleIndex).first, this, tree)());
        }

      {% for contextName in contextNames %}
        virtual  T visit ( FormalParser::{{ contextName }} * ctx ) = 0;
      {% endfor %}
    };

template <class T>
const typename AstVisitor<T>::RouteMap AstVisitor<T>::routing_ = {
      {% for rule in allRuleNames %}
        std::make_pair(FormalParser::ContextId{{ rule }},  std::make_pair(&AstVisitor<T>::template dispatchHelper<FormalParser::{{ rule }}Context> , &is<FormalParser::{{ rule }}Context>)),
      {% endfor %}
};
} // namespace cyclone::parser
#endif
